/*
* 
* @license	Apache License 2.0
* @author	Eugene Kuznetsov
*
*/
#include "CConfiguration.hpp"
#include "CDataBuffer.hpp"

CConfiguration* CConfiguration::m_pInstance = 0;

CConfiguration::CConfiguration() 
	: CDataStorage(CONFIGURATION_DATABASE_FILE)
{
	DBG_L1(("[%p] %s\n", this, __FUNCTION__));
}

CConfiguration::~CConfiguration()
{
	DBG_L1(("[%p] %s\n", this, __FUNCTION__));
}

void CConfiguration::CheckStorageConsistency(Base::eState& state)
{
	DBG_L2(("[%p] %s\n", this, __FUNCTION__));
	state = Base::STATE_UNKNOWN;
	if (getState() != Base::STATE_OK)
	{
		DBG_L2(("[%p] failed to check storage consistency\n", this));
		return;
	}
	CDataBuffer sql(SQL_CREATE_TABLE_DEVICES);
	state = Base::STATE_NOT_OK;
	if (ExecuteSQL(&sql) != Base::STATE_OK)
	{
		DBG_L2(("[%p] failed to create table `devices`\n", this));
		return;
	}
	sql.clear();
	sql.append(SQL_CREATE_TABLE_SERVERS);
	if (ExecuteSQL(&sql) != Base::STATE_OK)
	{
		DBG_L2(("[%p] failed to create table `servers`\n", this));
		return;
	}
	sql.clear();
	sql.append(SQL_CREATE_TRIGGER_DEVICES_ON_DEL_SERVER);
	if (ExecuteSQL(&sql) != Base::STATE_OK)
	{
		DBG_L2(("[%p] failed to create trigger `on_server_delete`\n", this));
		return;
	}
	sql.clear();
	sql.append(SQL_CREATE_TABLE_DEVICE_TYPES);
	if (ExecuteSQL(&sql) != Base::STATE_OK)
	{
		DBG_L2(("[%p] failed to create table `device_types`\n", this));
		return;
	}
	/*sql.clear();
	sql.append("INSERT INTO main.device_types VALUES (NULL, 'IntelliTrac')");
	ExecuteSQL(&sql);*/
	/*sql.clear();
	sql.append("INSERT INTO main.devices VALUES (1010000001, 1, 0, 0, 0)");
	ExecuteSQL(&sql);*/
	state = Base::STATE_OK;
}

bool CConfiguration::IsDeviceAllowed(Base::eState& state, UInt64 deviceID, Device::eType deviceType)
{
	DBG_L2(("[%p] %s(%llu, %d)\n", this, __FUNCTION__, deviceID, ToInt8(deviceType)));
	bool allow = false;
	state = Base::STATE_OK;
	const Chr* sql = sqlite3_mprintf(SQL_SELECT_DEVICE_TYPE, deviceID);
	//DBG_L2(("[%p] sql -> %s\n", this, sql));
	sqlite3_stmt* statement;
	Int16 result = sqlite3_prepare_v2(getDB(), sql, -1, &statement, NULL);
	if (result != SQLITE_OK)
	{
		state = Base::STATE_NOT_OK;
		DBG_L2(("[%p] sqlite3_prepare_v2 error -> %d\n", this, result));
	}
	if (sqlite3_step(statement) == SQLITE_ROW)
	{
		const UChr* type = sqlite3_column_text(statement, 0);
		switch (deviceType)
		{
		case Device::INTELLITRAC:
			if (_stricmp(reinterpret_cast<const Chr*>(type), "IntelliTrac") == 0)
			{
				allow = true;
			}
			break;
		case Device::TELTONIKA:
			if (_stricmp(reinterpret_cast<const Chr*>(type), "Teltonika") == 0)
			{
				allow = true;
			}
			break;
		default:
			state = Base::STATE_NOT_OK;
			break;
		}
	}
	sqlite3_finalize(statement);
	sqlite3_free(const_cast<Chr*>(sql));
	return allow;
}

Base::eState CConfiguration::GetServerForDevice(Device::eTargetServers targetServer, UInt64 deviceID, std::string& ip_address, UInt8& port)
{
	DBG_L2(("[%p] %s(%d, %llu)\n", this, __FUNCTION__, ToInt8(targetServer) + 1, deviceID));
	Base::eState state = Base::STATE_UNKNOWN;
	const Chr* sql = sqlite3_mprintf(SQL_SELECT_DEVICE_SERVER, ToInt8(targetServer) + 1, deviceID, ToInt8(targetServer) + 1);
	//DBG_L2(("[%p] sql -> %s\n", this, sql));
	sqlite3_stmt* statement;
	Int16 result = sqlite3_prepare_v2(getDB(), sql, -1, &statement, NULL);
	if (result != SQLITE_OK)
	{
		state = Base::STATE_NOT_OK;
		DBG_L2(("[%p] sqlite3_prepare_v2 error -> %d\n", this, result));
	}
	if (sqlite3_step(statement) == SQLITE_ROW)
	{
		std::string ipaddr(reinterpret_cast<const Chr*>(sqlite3_column_text(statement, 0)));
		ip_address = ipaddr;
		port = sqlite3_column_int(statement, 1);
		state = Base::STATE_OK;
	}
	sqlite3_finalize(statement);
	sqlite3_free(const_cast<Chr*>(sql));
	return state;
}

CConfiguration* CConfiguration::getInstance()
{
	if (m_pInstance == 0)
	{
		m_pInstance = new CConfiguration();
		Base::eState state;
		m_pInstance->CheckStorageConsistency(state);
		if (state != Base::STATE_OK)
		{
			removeInstance();
		}
	}
	return m_pInstance;
}

void CConfiguration::removeInstance()
{
	if (m_pInstance != 0)
	{
		delete m_pInstance;
		m_pInstance = 0;
	}
}
